/*
*	File: Exporter.cpp
*	Date: 12/09/2011
*	Author: Ivan Gao
*	Desc:
*/
#include "Exporter.h"
#include "Saver.h"
SExporter::SExporter () : m_pExpInterface (NULL), m_pInterface(NULL), m_pCoreSkeleton (NULL)
{

}

SExporter::SExporter (ExpInterface * pExp, Interface * pI) : m_pExpInterface (pExp), m_pInterface (pI), m_pCoreSkeleton (NULL)
{

}

SExporter::~SExporter ()
{
	if (m_pCoreSkeleton)
	{
		delete m_pCoreSkeleton;
		m_pCoreSkeleton = NULL;
	} // if
}

bool SExporter::ExportSkeleton(const char * pFileName)
{
	if (m_pCoreSkeleton)
	{
		delete m_pCoreSkeleton;
		m_pCoreSkeleton = NULL;
	} // if
	m_pCoreSkeleton = new SCoreSkeleton();
	if (!m_pCoreSkeleton)
	{
		return false;
	} // if
	Interval interval;
	Point3 ambient = m_pInterface->GetAmbient(m_pInterface->GetTime(), interval);
	SVector3 vAmbient (ambient.x, ambient.y, ambient.z);
	m_pCoreSkeleton->SetSceneAmbientColor(vAmbient);

	for (int nNodeId = 0; nNodeId < m_pInterface->GetSelNodeCount (); nNodeId++)
	{
		if (!AddNode (m_pInterface->GetSelNode(nNodeId), -1))
		{
			return false;
		} // if
	} // if
	SSaver::ExportSkeletonFile(pFileName, m_pCoreSkeleton);
	return true;
} // ExportSkeleton

bool SExporter::AddNode(INode * pNode, int nParentId)
{
	if (!pNode)
	{
		return false;
	} // if
	string strBoneName = pNode->GetName();
	SCoreBone * pCoreBone = new SCoreBone (strBoneName.c_str());
	if (pCoreBone)
	{
		pCoreBone->SetParentId(nParentId);
		// Get the translation and the rotation of bone space of the bone.
		SVector3 translationBoneSpace;
		SQuaternion rotationBoneSpace;

		SVector3 translation;
		SQuaternion rotation;
		Matrix3 matrix = pNode->GetNodeTM (SecToTicks (0.0f));
		Matrix3 tmpMatrix(Inverse(matrix));
		Point3 p = tmpMatrix.GetTrans();
		translationBoneSpace.SetValue(p.x, p.y, p.z);
		Quat q (tmpMatrix);
		rotation.Set(&q[0]);
		pCoreBone->SetTranslationBoneSpace(translationBoneSpace);
		pCoreBone->SetRotationBoneSpace (rotationBoneSpace);
		// Get the translation and rotation of the bone.
		if (nParentId != -1)
		{
			Matrix3 parentMatrix = pNode->GetParentTM(SecToTicks(0.0f));
			Matrix3 relativeMatrix = matrix * Inverse (parentMatrix);
			p = relativeMatrix.GetTrans();
			translation.SetValue(p.x, p.y, p.z);
			q = relativeMatrix;
			rotation.Set(&q[0]);
			pCoreBone->SetTranslation (translation);
			pCoreBone->SetRotation (rotation);
		}
		else
		{
			p = matrix.GetTrans ();
			q = matrix;
			translation.SetValue (p.x, p.y, p.z);
			rotation.Set (&q[0]);
			pCoreBone->SetTranslation (translation);
			pCoreBone->SetRotation (rotation);

		} // if

		int nId = m_pCoreSkeleton->AddCoreBone(pCoreBone);
		if (nParentId != -1)
		{
			SCoreBone * pParentCoreBone = m_pCoreSkeleton->GetCoreBone(nParentId);
			if (!pParentCoreBone)
			{
				pParentCoreBone->AddChildId (nId);
			}
			else
			{
				return false;
			} // if
		} // if
	} // if
	return true;
} // AddNode

bool SExporter::ExportMaterial (const char * pFileName)
{
	SCoreMaterialSet coreMaterialSet;
	int nMaterialCount = 0;
	nMaterialCount = GetMaterialCount ();
	coreMaterialSet.Reserve(nMaterialCount);
	GetMaterial (coreMaterialSet);
	if (!SSaver::ExportCoreMaterialSet(pFileName, coreMaterialSet))
	{
		return false;
	}
	return true;
} // ExporterMaterial

int SExporter::GetMaterialCount()
{
	int nCount = 0;
	int nBaseId = 0;
	for (; nBaseId < 24; nBaseId ++)
	{
		MtlBase * pBase = m_pInterface->GetMtlSlot (nBaseId);
		// Check if it is a material
		if (pBase->SuperClassID () == MATERIAL_CLASS_ID)
		{
			Mtl * pMtl = (Mtl *)pBase;
			nCount += GetSubMaterialCount(pMtl);
		} // if
	} // for
	return nCount;
} // GetMaterialCount


int SExporter::GetSubMaterialCount (Mtl * pMtl)
{
	int nCount = 0;
	if (pMtl->ClassID () == Class_ID (DMTL_CLASS_ID, 0))
	{
		return 1;
	} // if
	if (pMtl->IsMultiMtl ())
	{
		int nSubMtlId = 0;
		for (; nSubMtlId < pMtl->NumSubMtls (); nSubMtlId ++)
		{
			nCount += GetSubMaterialCount(pMtl->GetSubMtl(nSubMtlId));
		} // for
	} // if
	return nCount;
} // GetSubMaterialCount

void SExporter::GetMaterial(SCoreMaterialSet & rMaterialSet)
{
	// Get the material bases from the material editor slots
	for (int nBaseId = 0; nBaseId < 24; ++ nBaseId)
	{
		MtlBase * pBase = m_pInterface->GetMtlSlot (nBaseId);
		// Check if it is a material
		if (pBase->SuperClassID() == MATERIAL_CLASS_ID)
		{
			Mtl * pMtl = (Mtl *)pBase;
			GetSubMaterial(pMtl, rMaterialSet);
		} // if
	} // for
} // if

void SExporter::GetSubMaterial (Mtl * pMtl, SCoreMaterialSet & rMaterialSet)
{
	// Check if we have a standard material
	if (pMtl->ClassID () == Class_ID (DMTL_CLASS_ID, 0))
	{
		SCoreMaterial coreMaterial;
		SetCoreMaterial(coreMaterial, (StdMat *)pMtl);
		rMaterialSet.AddCoreMaterial(coreMaterial);
	}
	else
	{
		if (pMtl->IsMultiMtl ())
		{
			int nMatId = 0;
			for (; nMatId < pMtl->NumSubMtls (); nMatId++)
			{
				GetSubMaterial(pMtl->GetSubMtl(nMatId), rMaterialSet);
			} // for
		} // if
	} // if
} // GetSubMaterial

void SetCoreMaterial (SCoreMaterial & rMaterial, StdMat * pMat)
{
	// Set the ambient color.
	Color col = pMat->GetAmbient (0);
	SCoreMaterial::Color color;
	color.red	= (unsigned char)(col.r * 255);
	color.green	= (unsigned char)(col.g * 255);
	color.blue	= (unsigned char)(col.b * 255);
	color.alpha = 0;
	rMaterial.SetAmbientColor(color);

	// Get the diffuse color.
	col = pMat->GetDiffuse(0);
	color.red	= (unsigned char)(col.r * 255);
	color.green	= (unsigned char)(col.g * 255);
	color.blue	= (unsigned char)(col.b * 255);
	color.alpha = 0;
	rMaterial.SetDiffuseColor(color);

	// Set the specular color.
	col = pMat->GetSpecular(0);
	color.red	= (unsigned char)(col.r * 255);
	color.green	= (unsigned char)(col.g * 255);
	color.blue	= (unsigned char)(col.b * 255);
	color.alpha = 0;
	rMaterial.SetSpecularColor(color);

	// Set the shininess of specular.
	float shininess = pMat->GetShininess(0);
	rMaterial.SetShininess(shininess);

	// Set the material name.
	rMaterial.SetName(pMat->GetName().data());

	// Set the maps of the material.
	int nMapCount = 0;
	// Get the num of maps of the material
	int nMapId = 0;
	for (; nMapId < pMat->NumSubTexmaps (); nMapId++)
	{
		// Get the texture map.
		Texmap * pTexMap = pMat->GetSubTexmap(nMapId);
		// Check if the map is valid.
		if (pTexMap && pMat->MapEnabled(nMapId))
		{
			++ nMapCount;
		} // if
	} // for
	rMaterial.Reserve(nMapCount);
	for (nMapId = 0; nMapId < pMat->NumSubTexmaps (); ++ nMapId)
	{
		// Get the texture map.
		Texmap * pTexMap = pMat->GetSubTexmap(nMapId);
		// Check if the map is valid
		if (pTexMap && pMat->MapEnabled(nMapId))
		{
			if (pTexMap->ClassID() == Class_ID(BMTEX_CLASS_ID, 0))
			{
				string strFileName = ((BitmapTex *)pTexMap)->GetMapName ();
				// extract the filename.
				string::size_type pos = strFileName.find_last_of("\\/:");
				SCoreMaterial::Map m;
				if (pos == string::npos)
				{
					m.strFileName = strFileName;
				}
				else
				{
					m.strFileName = strFileName.substr(pos+1);
				} // if
				m.strType = pMat->GetSubTexmapSlotName(nMapId).data();
				rMaterial.AddMap(m);
			} // if
		} // if
	} // for

} // SetCoreMaterial

